home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / javax / swing / RepaintManager.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  16.5 KB  |  564 lines  |  [TEXT/CWIE]

  1. /*
  2.  * @(#)RepaintManager.java    1.32 98/08/26
  3.  *
  4.  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14. package javax.swing;
  15.  
  16.  
  17. import java.awt.*;
  18. import java.awt.event.*;
  19. import java.util.*;
  20. import java.applet.*;
  21.  
  22.  
  23. /**
  24.  * This class manages repaint requests, allowing the number
  25.  * of repaints to be minimized, for example by collapsing multiple 
  26.  * requests into a single repaint for members of a component tree.
  27.  *
  28.  * @version 1.32 08/26/98
  29.  * @author Arnaud Weber
  30.  */
  31. public class RepaintManager 
  32. {
  33.     Hashtable dirtyComponents = new Hashtable();
  34.     Hashtable tmpDirtyComponents = new Hashtable();
  35.     Vector    invalidComponents;
  36.     boolean   doubleBufferingEnabled = true;
  37.     Image     doubleBuffer;
  38.     Dimension doubleBufferMaxSize;
  39.     Dimension doubleBufferSize;
  40.  
  41.     private static final Object repaintManagerKey = RepaintManager.class;
  42.  
  43.     /** 
  44.      * Return the RepaintManager for the calling thread given a Component.
  45.      * 
  46.      * @param c a Component -- unused in the default implementation, but could
  47.      *          be used by an overridden version to return a different RepaintManager
  48.      *          depending on the Component
  49.      * @return the RepaintManager object
  50.      */
  51.     public static RepaintManager currentManager(Component c) {
  52.         RepaintManager result = (RepaintManager) SwingUtilities.appContextGet(repaintManagerKey);
  53.         if(result == null) {
  54.             result = new RepaintManager();
  55.             SwingUtilities.appContextPut(repaintManagerKey, result);
  56.         }
  57.     return result;
  58.     }
  59.  
  60.     
  61.     /**
  62.      * Return the RepaintManager for the calling thread given a JComponent.
  63.      * <p>
  64.      * Note: This method exists for backward binary compatibility with earlier
  65.      * versions of the Swing library. It simply returns the result returned by
  66.      * {@link #currentManager(Component)}. 
  67.      *
  68.      * @param c a JComponent -- unused
  69.      * @return the RepaintManager object
  70.      */
  71.     public static RepaintManager currentManager(JComponent c) {
  72.     return currentManager((Component)c);
  73.     }
  74.  
  75.  
  76.     /**
  77.      * Set the RepaintManager that should be used for the calling 
  78.      * thread. <b>aRepaintManager</b> will become the current RepaintManager
  79.      * for the calling thread's thread group.
  80.      * @param aRepaintManager  the RepaintManager object to use
  81.      */
  82.     public static void setCurrentManager(RepaintManager aRepaintManager) {
  83.         if (aRepaintManager != null) {
  84.             SwingUtilities.appContextPut(repaintManagerKey, aRepaintManager);
  85.         } else {
  86.             SwingUtilities.appContextRemove(repaintManagerKey);
  87.         }
  88.     }
  89.  
  90.     /** 
  91.      * Create a new RepaintManager instance. You rarely call this constructor.
  92.      * directly. To get the default RepaintManager, use 
  93.      * RepaintManager.currentManager(JComponent) (normally "this").
  94.      */
  95.     public RepaintManager() {
  96.         doubleBufferMaxSize = Toolkit.getDefaultToolkit().getScreenSize();
  97.     }
  98.  
  99.  
  100.     /**
  101.      * Mark the component as in need of layout and queue a runnable
  102.      * for the event dispatching thread that will validate the components
  103.      * first isValidateRoot() ancestor. 
  104.      * 
  105.      * @see JComponent#isValidateRoot
  106.      * @see #removeInvalidComponent
  107.      */
  108.     public synchronized void addInvalidComponent(JComponent invalidComponent) 
  109.     {
  110.         Component validateRoot = null;
  111.  
  112.     /* Find the first JComponent ancestor of this component whose
  113.      * isValidateRoot() method returns true.  
  114.      */
  115.         for(Component c = invalidComponent; c != null; c = c.getParent()) {
  116.         if ((c instanceof CellRendererPane) || (c.getPeer() == null)) {
  117.         return;
  118.         }
  119.         if ((c instanceof JComponent) && (((JComponent)c).isValidateRoot())) {
  120.         validateRoot = c;
  121.         break;
  122.         }
  123.     }
  124.         
  125.     /* There's no validateRoot to apply validate to, so we're done.
  126.      */
  127.     if (validateRoot == null) {
  128.         return;
  129.     }
  130.  
  131.     /* If the validateRoot and all of its ancestors aren't visible
  132.      * then we don't do anything.  While we're walking up the tree
  133.      * we find the root Window or Applet.
  134.      */
  135.     Component root = null;
  136.     
  137.     for(Component c = validateRoot; c != null; c = c.getParent()) {
  138.         if (!c.isVisible() || (c.getPeer() == null)) {
  139.         return;
  140.         }
  141.         if ((c instanceof Window) || (c instanceof Applet)) {
  142.         root = c;
  143.         break;
  144.         }
  145.     }
  146.  
  147.     if (root == null) {
  148.         return;
  149.     }
  150.        
  151.     /* Lazily create the invalidateComponents vector and add the
  152.      * validateRoot if it's not there already.  If this validateRoot
  153.      * is already in the vector, we're done.
  154.      */
  155.     if (invalidComponents == null) {
  156.         invalidComponents = new Vector();
  157.     }
  158.     else {
  159.         int n = invalidComponents.size();
  160.         for(int i = 0; i < n; i++) {
  161.         if(validateRoot == (Component)(invalidComponents.elementAt(i))) {
  162.             return;
  163.         }
  164.         }
  165.     }
  166.     invalidComponents.addElement(validateRoot);
  167.  
  168.     /* Queues a Runnable that calls RepaintManager.validateInvalidComponents() 
  169.      * and RepaintManager.paintDirtyRegions() with SwingUtilities.invokeLater().
  170.      */
  171.     SystemEventQueueUtilities.queueComponentWorkRequest(root);
  172.     }
  173.  
  174.  
  175.     /** 
  176.      * Remove a component from the list of invalid components.
  177.      * 
  178.      * @see #addInvalidComponent
  179.      */
  180.     public synchronized void removeInvalidComponent(JComponent component) {
  181.         if(invalidComponents != null) {
  182.             int index = invalidComponents.indexOf(component);
  183.             if(index != -1) {
  184.                 invalidComponents.removeElementAt(index);
  185.             }
  186.         }
  187.     }
  188.  
  189.  
  190.     /** 
  191.      * Add a component in the list of components that should be refreshed.
  192.      * If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i> 
  193.      * will be unioned with the region that should be redrawn. 
  194.      * 
  195.      * @see JComponent#repaint
  196.      */
  197.     public synchronized void addDirtyRegion(JComponent c, int x, int y, int w, int h) 
  198.     {
  199.     /* Special cases we don't have to bother with.
  200.      */
  201.         if ((w <= 0) || (h <= 0) || (c == null)) {
  202.             return;
  203.         }
  204.  
  205.     if ((c.getWidth() <= 0) || (c.getHeight() <= 0)) {
  206.         return;
  207.     }
  208.  
  209.     /* Make sure that c and all it ancestors (up to an Applet or
  210.      * Window) are visible.  This loop has the same effect as 
  211.      * checking c.isShowing() (and note that it's still possible 
  212.      * that c is completely obscured by an opaque ancestor in 
  213.      * the specified rectangle).
  214.      */
  215.     Component root = null;
  216.  
  217.     for (Container p = c; p != null; p = p.getParent()) {
  218.         if (!p.isVisible() || (p.getPeer() == null)) {
  219.         return;
  220.         }
  221.         if ((p instanceof Window) || (p instanceof Applet)) {
  222.         root = p;
  223.         break;
  224.         }
  225.     }
  226.  
  227.     Rectangle r = (Rectangle)dirtyComponents.get(c);
  228.     if (r == null) {
  229.         dirtyComponents.put(c, new Rectangle(x, y, w, h));
  230.     }
  231.     else {
  232.         SwingUtilities.computeUnion(x, y, w, h, r);
  233.     }
  234.  
  235.  
  236.     /* Queues a Runnable that calls validateInvalidComponents() and
  237.      * rm.paintDirtyRegions() with SwingUtilities.invokeLater().
  238.      */
  239.     SystemEventQueueUtilities.queueComponentWorkRequest(root);
  240.     }
  241.     
  242.  
  243.     /** Return the current dirty region for a component.
  244.      *  Return an empty rectangle if the component is not
  245.      *  dirty.
  246.      */
  247.     public Rectangle getDirtyRegion(JComponent aComponent) {
  248.     Rectangle r = null;
  249.     synchronized(this) {
  250.         r = (Rectangle)dirtyComponents.get(aComponent);
  251.     }
  252.     if(r == null)
  253.         return new Rectangle(0,0,0,0);
  254.     else
  255.         return new Rectangle(r);
  256.     }
  257.  
  258.     /** 
  259.      * Mark a component completely dirty. <b>aComponent</b> will be
  260.      * completely painted during the next paintDirtyRegions() call.
  261.      */
  262.     public void markCompletelyDirty(JComponent aComponent) {
  263.     addDirtyRegion(aComponent,0,0,Integer.MAX_VALUE,Integer.MAX_VALUE);
  264.     }
  265.         
  266.     /** 
  267.      * Mark a component completely clean. <b>aComponent</b> will not
  268.      * get painted during the next paintDirtyRegions() call.
  269.      */
  270.     public void markCompletelyClean(JComponent aComponent) {
  271.     synchronized(this) {
  272.         dirtyComponents.remove(aComponent);
  273.     }
  274.     }
  275.  
  276.     /** 
  277.      * Convenience method that returns true if <b>aComponent</b> will be completely
  278.      * painted during the next paintDirtyRegions(). If computing dirty regions is
  279.      * expensive for your component, use this method and avoid computing dirty region
  280.      * if it return true.
  281.      */
  282.     public boolean isCompletelyDirty(JComponent aComponent) {
  283.     Rectangle r;
  284.     
  285.     r = getDirtyRegion(aComponent);
  286.     if(r.width == Integer.MAX_VALUE &&
  287.        r.height == Integer.MAX_VALUE)
  288.         return true;
  289.     else
  290.         return false;
  291.     }
  292.  
  293.  
  294.     /** 
  295.      * Validate all of the components that have been marked invalid.
  296.      * @see #addInvalidComponent
  297.      */
  298.     public void validateInvalidComponents() {
  299.         Vector ic;
  300.         synchronized(this) {
  301.             if(invalidComponents == null) {
  302.                 return;
  303.         }
  304.             ic = invalidComponents;
  305.             invalidComponents = null;
  306.         }
  307.     int n = ic.size();
  308.         for(int i = 0; i < n; i++) {
  309.             ((Component)ic.elementAt(i)).validate();
  310.         }
  311.     }
  312.     
  313.  
  314.     /**
  315.      * Paint all of the components that have been marked dirty.
  316.      * 
  317.      * @see #addDirtyRegion
  318.      */
  319.     public void paintDirtyRegions() {
  320.         int i, count;
  321.     Vector roots;
  322.         JComponent dirtyComponent;
  323.  
  324.     synchronized(this) {  // swap for thread safety
  325.         Hashtable tmp = tmpDirtyComponents;
  326.         tmpDirtyComponents = dirtyComponents;
  327.         dirtyComponents = tmp;
  328.         dirtyComponents.clear();
  329.     }
  330.  
  331.         count = tmpDirtyComponents.size();
  332.         if (count == 0) {
  333.             return;
  334.         } 
  335.  
  336.         Rectangle rect;
  337.         int localBoundsX = 0;
  338.         int localBoundsY = 0;
  339.         int localBoundsH = 0;
  340.         int localBoundsW = 0;    
  341.         Enumeration keys;
  342.  
  343.         roots = new Vector(count);
  344.         keys = tmpDirtyComponents.keys();
  345.  
  346.         while(keys.hasMoreElements()) {
  347.             dirtyComponent = (JComponent) keys.nextElement();
  348.             collectDirtyComponents(tmpDirtyComponents, dirtyComponent, roots);
  349.         }
  350.  
  351.         count = roots.size();
  352.         //        System.out.println("roots size is " + count);
  353.         for(i=0 ; i < count ; i++) {
  354.             dirtyComponent = (JComponent) roots.elementAt(i);
  355.             rect = (Rectangle) tmpDirtyComponents.get(dirtyComponent);
  356.             //            System.out.println("Should refresh :" + rect);
  357.             localBoundsH = dirtyComponent.getHeight();
  358.             localBoundsW = dirtyComponent.getWidth();
  359.  
  360.             SwingUtilities.computeIntersection(localBoundsX, 
  361.                            localBoundsY,
  362.                            localBoundsW,
  363.                            localBoundsH,
  364.                            rect);
  365.             // System.out.println("** paint of " + dirtyComponent + rect);
  366.             dirtyComponent.paintImmediately(rect.x,rect.y,rect.width,rect.height);
  367.         }
  368.     tmpDirtyComponents.clear();
  369.     }
  370.  
  371.  
  372.     Rectangle tmp = new Rectangle();
  373.  
  374.     void collectDirtyComponents(Hashtable dirtyComponents,
  375.                 JComponent dirtyComponent,
  376.                 Vector roots) {
  377.         int dx, dy, rootDx, rootDy;
  378.         Component component, rootDirtyComponent,parent;
  379.     //Rectangle tmp;
  380.         Rectangle cBounds;
  381.         boolean opaqueAncestorFound = false;
  382.  
  383.         // Find the highest parent which is dirty.  When we get out of this
  384.         // rootDx and rootDy will contain the translation from the
  385.         // rootDirtyComponent's coordinate system to the coordinates of the
  386.         // original dirty component.  The tmp Rect is also used to compute the
  387.         // visible portion of the dirtyRect.
  388.  
  389.         component = rootDirtyComponent = dirtyComponent;
  390.  
  391.         cBounds = dirtyComponent._bounds;
  392.  
  393.         dx = rootDx = 0;
  394.         dy = rootDy = 0;
  395.         tmp = new Rectangle((Rectangle) dirtyComponents.get(dirtyComponent));
  396.  
  397.         // System.out.println("Collect dirty component for bound " + tmp + 
  398.         //                                   "component bounds is " + cBounds);;
  399.         SwingUtilities.computeIntersection(0,0,cBounds.width,cBounds.height,tmp);
  400.  
  401.         if (tmp.isEmpty()) {
  402.             // System.out.println("Empty 1");
  403.             return;
  404.         } 
  405.  
  406.         if(dirtyComponent.isOpaque())
  407.             opaqueAncestorFound = true;
  408.  
  409.         for(;;) {
  410.             parent = component.getParent();
  411.             if(parent == null) 
  412.                 break;
  413.  
  414.             if(!(parent instanceof JComponent))
  415.                 break;
  416.  
  417.             component = parent;
  418.  
  419.             if(((JComponent)component).isOpaque())
  420.                 opaqueAncestorFound = true;
  421.  
  422.             dx += cBounds.x;
  423.             dy += cBounds.y;
  424.             tmp.setLocation(tmp.x + cBounds.x,
  425.                             tmp.y + cBounds.y);
  426.  
  427.             cBounds = ((JComponent)component)._bounds;
  428.             tmp = SwingUtilities.computeIntersection(0,0,cBounds.width,cBounds.height,tmp);
  429.  
  430.             if (tmp.isEmpty()) {
  431.                 // System.out.println("Empty 2");
  432.                 return;
  433.             }
  434.  
  435.             if (dirtyComponents.get(component) != null) {
  436.                 rootDirtyComponent = component;
  437.                 rootDx = dx;
  438.                 rootDy = dy;
  439.             }
  440.         } 
  441.  
  442.         if (dirtyComponent != rootDirtyComponent) {
  443.         Rectangle r;
  444.             tmp.setLocation(tmp.x + rootDx - dx,
  445.                 tmp.y + rootDy - dy);
  446.         r = (Rectangle)dirtyComponents.get(rootDirtyComponent);
  447.         SwingUtilities.computeUnion(tmp.x,tmp.y,tmp.width,tmp.height,r);
  448.         }
  449.  
  450.         // If we haven't seen this root before, then we need to add it to the
  451.         // list of root dirty Views.
  452.  
  453.         if (!roots.contains(rootDirtyComponent)) 
  454.             roots.addElement(rootDirtyComponent);    
  455.     }
  456.  
  457.  
  458.     /**
  459.      * Returns a string that displays and identifies this
  460.      * object's properties.
  461.      *
  462.      * @return a String representation of this object
  463.      */
  464.     public synchronized String toString() {
  465.     StringBuffer sb = new StringBuffer();
  466.     if(dirtyComponents != null) 
  467.         sb.append("" + dirtyComponents);
  468.         return sb.toString();
  469.     }
  470.  
  471.     /**
  472.      * Return the offscreen buffer that should be used as a double buffer with the component <code>c</code>
  473.      * By default there is a double buffer per RepaintManager.
  474.      * The buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>
  475.      * This happens when the maximum double buffer size as been set for the receiving
  476.      * repaint manager.
  477.      */
  478.     public Image getOffscreenBuffer(Component c,int proposedWidth,int proposedHeight) {
  479.         Image result;
  480.         int width,height;
  481.  
  482.         if(proposedWidth > doubleBufferMaxSize.width)
  483.             width = doubleBufferMaxSize.width;
  484.         else
  485.             width = proposedWidth;
  486.  
  487.         if(proposedHeight > doubleBufferMaxSize.height) 
  488.             height =doubleBufferMaxSize.height;
  489.         else
  490.             height = proposedHeight;
  491.  
  492.         if(doubleBuffer != null) {
  493.             if(doubleBuffer.getWidth(null) < width || doubleBuffer.getHeight(null) < height) {
  494.                 doubleBuffer = null;
  495.         }
  496.         }
  497.  
  498.     int go_width = width;
  499.         int go_height = height;
  500.  
  501.         if(doubleBuffer != null) {
  502.            go_width = doubleBufferSize.width;
  503.        go_height = doubleBufferSize.height;
  504.  
  505.        if( doubleBufferSize.width < width ) {
  506.            go_width = width;
  507.            doubleBuffer = null;
  508.        }
  509.        
  510.        if( doubleBufferSize.height < height ) {
  511.            go_height = height;
  512.            doubleBuffer = null;
  513.        }
  514.         }
  515.  
  516.     if(doubleBuffer == null) {
  517.             doubleBuffer = c.createImage( go_width , go_height );
  518.             doubleBufferSize = new Dimension( go_width , go_height );
  519.     }
  520.  
  521.         return doubleBuffer;
  522.     }
  523.  
  524.     /** Set the maximum double buffer size. **/
  525.     public void setDoubleBufferMaximumSize(Dimension d) {
  526.         doubleBufferMaxSize = d;
  527.         if(doubleBuffer != null) {
  528.             if(doubleBuffer.getWidth(null) > d.width || doubleBuffer.getHeight(null) > d.height) {
  529.         doubleBuffer = null;
  530.         }
  531.         }
  532.     }
  533.  
  534.     /**
  535.      * Returns the maximum double buffer size.
  536.      *
  537.      * @return a Dimension object representing the maximum size
  538.      */
  539.     public Dimension getDoubleBufferMaximumSize() {
  540.         return doubleBufferMaxSize;
  541.     }
  542.  
  543.     /**
  544.      * Enables or disables double buffering.
  545.      *
  546.      * @param aFlag  true to activate double buffering
  547.      */
  548.     public void setDoubleBufferingEnabled(boolean aFlag) {
  549.         doubleBufferingEnabled = aFlag;
  550.     if(!doubleBufferingEnabled) {
  551.       doubleBuffer = null;
  552.     }
  553.     }
  554.  
  555.     /**
  556.      * Returns true if this object is double buffered.
  557.      *
  558.      * @return true if this object is double buffered
  559.      */
  560.     public boolean isDoubleBufferingEnabled() {
  561.       return doubleBufferingEnabled;
  562.     }
  563. }
  564.